package com.app.core.signature;

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import lombok.NoArgsConstructor;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

@NoArgsConstructor
public abstract class AbstractSignature {
    public static final String CA_KEY = "ca-app-key";
    public static final String CA_SIGNATURE_HEADERS = "ca-signature-headers";
    public static final String CA_SIGNATURE = "ca-signature";
    public static final String CA_NONCE = "ca-nonce";
    public static final String CA_TIMESTAMP = "ca-timestamp";
    public static final String CA_SIGNATURE_CONTENT_MD5 = "ca-content-md5";
    public static final String ENCODING = "UTF-8";
    public static final String HMAC_SHA256 = "HmacSHA256";
    public static char LF = '\n';
    protected String path;
    protected String httpMethod;
    protected Map<String, String> headers;
    protected String secret;
    protected String signatureMethod;

    public AbstractSignature(final String path, final String httpMethod, final Map<String, String> headers, final String signatureMethod) {
        this.path = path;
        this.httpMethod = httpMethod;
        this.headers = null == headers ? new HashMap() : headers;
        this.signatureMethod = null == signatureMethod ? HMAC_SHA256 : signatureMethod;
    }

    public void build(final Map<String, Object> paramsMap) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
        this.build(paramsMap, null);
    }

    /**
     * @author qiangt
     * @date 2023/8/29
     * @apiNote 构造签名明文并加密，与header中的ca-signature比较
     */
    public abstract void build(Map<String, Object> paramsMap, Map<String, Object> formParamsMap) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException;

    public void build() throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
        this.build(null, null);
    }

    /** 构造签名密文 */
    protected String stringToSign(final String stringToSign, final String signatureMethod, final String secret) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
        // final Mac hmacSha = Mac.getInstance(signatureMethod);
        // final byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
        // hmacSha.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, signatureMethod));
        // return new String(Base64.encodeBase64(hmacSha.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8);
        HMac mac = new HMac(HmacAlgorithm.valueOf(signatureMethod), secret.getBytes(StandardCharsets.UTF_8));
        String digestHex = mac.digestHex(stringToSign);
        return cn.hutool.core.codec.Base64.encode(digestHex.getBytes(StandardCharsets.UTF_8));
    }

    protected String base64AndMD5(final byte[] bytes) {
        if (bytes == null) {
            throw new IllegalArgumentException("bytes can not be null");
        } else {
            try {
                return Base64.encode(SecureUtil.md5(new String(bytes, StandardCharsets.UTF_8)));
            } catch (final Exception var5) {
                throw new IllegalArgumentException("unknown algorithm MD5");
            }
        }
    }

    /** 构造签名明文 */
    protected String buildStringToSign(final Map<String, Object> paramsMap, final Map<String, Object> formParamsMap) {
        final StringBuilder sb = new StringBuilder();
        sb.append(this.httpMethod.toUpperCase()).append(LF);
        if (this.headers.get(CA_SIGNATURE_CONTENT_MD5) != null) {
            sb.append(this.headers.get(CA_SIGNATURE_CONTENT_MD5));
        }
        sb.append(LF);

        //        if (this.headers.get("content-type") != null) {
        //            sb.append((String)this.headers.get("content-type"));
        //        }
        //        sb.append(LF);

        //        if (this.headers.get("date") != null) {
        //            sb.append((String)this.headers.get("date"));
        //        }
        //        sb.append(LF);

        sb.append(this.buildHeaders());
        sb.append(this.buildResource(this.path, paramsMap, formParamsMap));
        return sb.toString();
    }

    protected abstract String buildHeaders();

    /** 组装@RequestParam参数，构造完整请求路径：path?a=1&b=2 */
    private String buildResource(final String path, final Map<String, Object> paramsMap, final Map<String, Object> formParamsMap) {
        final StringBuilder result = new StringBuilder();
        result.append(path);
        final TreeMap<String, String> parameter = new TreeMap();
        Iterator paramsIt;
        Map.Entry entry;
        if (null != paramsMap && paramsMap.size() > 0) {
            paramsIt = paramsMap.entrySet().iterator();

            while (paramsIt.hasNext()) {
                entry = (Map.Entry) paramsIt.next();
                if (entry.getValue() != null) {
                    parameter.put((String) entry.getKey(), entry.getValue().toString());
                }
            }
        }

        if (null != formParamsMap && formParamsMap.size() > 0) {
            paramsIt = formParamsMap.entrySet().iterator();

            while (paramsIt.hasNext()) {
                entry = (Map.Entry) paramsIt.next();
                if (entry.getValue() != null) {
                    parameter.put((String) entry.getKey(), entry.getValue().toString());
                }
            }
        }

        if (parameter.size() > 0) {
            result.append("?");
            boolean isFirst = true;
            final Iterator parameterIt = parameter.keySet().iterator();

            while (parameterIt.hasNext()) {
                final String key = (String) parameterIt.next();
                if (!isFirst) {
                    result.append("&");
                } else {
                    isFirst = false;
                }

                result.append(key);
                final String value = parameter.get(key);
                if (null != value && !"".equals(value)) {
                    result.append("=").append(value);
                }
            }
        }

        return result.toString();
    }

    protected void addCaToHeaders(final String key, final String value) {
        this.headers.put(key, value);
    }

    public Map<String, String> getHeaders() {
        return this.headers;
    }

    /** 获取header中ca-signature的值，并删除header中ca-signature */
    public String getSign() {
        return this.headers.remove(CA_SIGNATURE);
    }

    public String getKey() {
        return this.headers.get(CA_KEY);
    }

    protected String getContentMD5() {
        return this.headers.get(CA_SIGNATURE_CONTENT_MD5);
    }

    protected String getTimestamp() {
        return this.headers.getOrDefault(CA_TIMESTAMP, "0");
    }
}
